using System;
using System.Data;
using System.Data.SqlClient;
using PropertyExpression.Common;

namespace PropertyExpression.ControllableQuery
{
    public static class QueryContextExtensions
    {
        public static IParam<string> CreateStringParam(this SqlCommand it)
        {
            return it.CreateParam<string>(GetStringSqlParameterFunc(), _ => _);
        }

        public static IParam<IOption<string>> CreateOptionalStringParam(this SqlCommand it)
        {
            return it.CreateParam<IOption<string>>(GetStringSqlParameterFunc(), GetValueOrDbNull);
        }

        private static Func<string, SqlParameter> GetStringSqlParameterFunc()
        {
            return name => new SqlParameter(name, SqlDbType.NVarChar) { Size = -1 };
        }

        public static IParam<string> Param(this SqlCommand it, string value)
        {
            var param = it.CreateStringParam();
            param.Value = value;
            return param;
        }

        public static IParam<string> Param(this SqlCommand it, Func<string> valueFunc)
        {
            var param = it.CreateStringParam();
            param.ValueFunc = valueFunc;
            return param;
        }

        public static IParam<Guid> CreateGuidParam(this SqlCommand it)
        {
            return it.CreateParam<Guid>(name => new SqlParameter(name, SqlDbType.UniqueIdentifier), _ => _);
        }

        public static IParam<IOption<Guid>> CreateOptionalGuidParam(this SqlCommand it)
        {
            return it.CreateParam<IOption<Guid>>(name => new SqlParameter(name, SqlDbType.UniqueIdentifier), _ => _.GetValueOrDbNull());
        }

        public static IParam<Guid> Param(this SqlCommand it, Guid value)
        {
            var param = it.CreateGuidParam();
            param.Value = value;
            return param;
        }

        public static IParam<Guid> Param(this SqlCommand it, Func<Guid> valueFunc)
        {
            var param = it.CreateGuidParam();
            param.ValueFunc = valueFunc;
            return param;
        }

        public static IParam<IOption<Guid>> Param(this SqlCommand it, IOption<Guid> value)
        {
            var param = it.CreateOptionalGuidParam();
            param.Value = value;
            return param;
        }

        public static IParam<int> CreateIntParam(this SqlCommand it)
        {
            return it.CreateParam<int>(name => new SqlParameter(name, SqlDbType.Int), _ => _);
        }

        public static IParam<IOption<int>> CreateOptionalIntParam(this SqlCommand it)
        {
            return it.CreateParam<IOption<int>>(name => new SqlParameter(name, SqlDbType.Int), _ => _.GetValueOrDbNull());
        }

        public static IParam<int> Param(this SqlCommand it, int value)
        {
            var param = it.CreateIntParam();
            param.Value = value;
            return param;
        }

        public static IParam<IOption<int>> Param(this SqlCommand it, IOption<int> value)
        {
            var param = it.CreateOptionalIntParam();
            param.Value = value;
            return param;
        }

        public static IParam<int> Param(this SqlCommand it, Func<int> valueFunc)
        {
            var param = it.CreateIntParam();
            param.ValueFunc = valueFunc;
            return param;
        }

        public static object GetValueOrDbNull<T>(this IOption<T> value)
        {
            return value.Process<object>(_ => _, () => DBNull.Value);
        }
    }
}